---
title: 缓存
---

缓存是存在内存中的临时数据，将用户经常查询的数据放在缓存（内存）中，用户去查询数据就不用从磁盘上（关系型数据库数据文件）查询，从而提高查询效率，解决了高并发系统的性能问题。

* 减少和数据库的交互次数，减小系统开销，提高系统效率。
* 适用于经常查询并且不经常改变的数据。

查询要连接数据库，连接数据库会耗费资源，所以我们可以将查询到的数据放到缓存中，下次查询直接从缓存中取，不用再连接数据库，从而提高查询效率。

:::tip
数据库水平复制会有问题，因为数据库的数据是经常变化的，如果数据库中的数据发生了变化，缓存中的数据没有变化，就会出现数据不一致的问题。
:::

## MyBatis 缓存

* MyBatis 包含一个非常强大的查询缓存特性，它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
* MyBatis 系统中默认定义了两级缓存：一级缓存和二级缓存。
    * 默认情况下，只有一级缓存开启（也就是 session 级别的缓存），而二级缓存是需要手动开启的。
    * 一级缓存是 SqlSession 级别的缓存（sqlSession.close() 之后就无效了），而二级缓存是 mapper 级别的缓存。
    * 为了提高扩展性，MyBatis 定义了缓存接口 Cache，我们可以通过实现 Cache 接口来自定义二级缓存。

## 一级缓存

一级缓存默认是开启的，只在一次 SqlSession 中有效，也就是拿到连接到关闭连接这个过程中有效。

每个用户都有自己的 SqlSession，所以每个用户都有自己的一级缓存，不会影响其他用户。

一级缓存就是一个 Map

```java
package pub.wkq.dao;

import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import pub.wkq.utils.MyBatisUtils;

public class UserMapperTest {
    /**
     * <h2>测试获取全部用户</h2>
     */
    @Test
    public void testGetUserList() {
        try (SqlSession sqlSession = MyBatisUtils.getSqlSession()) {
            //这里sql只查询了一次，但是打印了两次，说明缓存生效了
            sqlSession.getMapper(UserMapper.class).getUserList().forEach(System.out::println);
            System.out.println("==================================");
            //修改用户
//            sqlSession.getMapper(UserMapper.class).updateUser(new pub.wkq.pojo.User(1, null, "456"));
            sqlSession.clearCache();
            sqlSession.getMapper(UserMapper.class).getUserList().forEach(System.out::println);
            sqlSession.commit();
        }
    }
}
```

缓存时效的情况：

1. 查询不同的东西
2. 增删改操作，可能会改变原来的数据，所以必定会刷新缓存。
3. 查询不同的 Mapper.xml
4. 手动清除缓存 `sqlSession.clearCache();`

## 二级缓存

1. 开启全局缓存
2. 开启当前 Mapper.xml 的二级缓存

```xml
<!--开启二级缓存-->
<cache eviction="FIFO" flushInterval="60000" readOnly="true" size="512" type="PERPETUAL"/>
```

FIFO 表示这是一个先进先出的缓存策略，flushInterval 表示缓存刷新间隔，单位为毫秒，readOnly 表示只读，size 表示缓存大小，单位为条，type
表示缓存策略，MyBatis 默认提供了三种缓存策略，分别是：

* LRU：最近最少使用，一般情况下，这个策略就可以满足大部分需求了。
* FIFO：先进先出，按照数据进入缓存的顺序来移除它们。
* SOFT：软引用，移除基于垃圾回收器状态和软引用规则的对象。
* WEAK：弱引用，更积极地移除基于垃圾收集器状态和弱引用规则的对象。


* 二级缓存也叫做全局缓存，一级缓存作用域太低了，所以诞生了二级缓存
* 基于 namespace 级别的缓存，一个名称空间，对应一个二级缓存；
* 工作机制：
    * 一个会话查询一条数据，这个数据就会被放在当前会话的一级缓存中；
    * 如果当前会话关闭了，这个会话对应的一级缓存就没有了；但我们想要的是，会话关闭了，一级缓存中的数据被保存到二级缓存中；
    * 新的会话查询信息，就可以从二级缓存中获取内容；
    * 不同的 mapper 查出的数据会放在自己对应的缓存（map）中。

:::tip
需要将实体类序列化！否则就会报错！
:::
**总结**

1. 只要开启了二级缓存，在同一个 Mapper 下就有效
2. 所有的数据都会先放在一级缓存中
3. 只有当会话提交或者关闭的时候，才会提交到二级缓存中
